// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"code.google.com/p/godeit/parser"
	"flag"
	"fmt"
	"github.com/cznic/strutil"
	"go/scanner"
	"go/token"
	"os"
)

type pat struct {
	states []int
	last   int
	msg    string
}

func states(src string) (sts []int, lst int) {
	fset := token.NewFileSet()
	file := fset.AddFile("x", fset.Base(), len(src))
	pass := 0
	parser.XErrorHook = func(yyS []int, last int) {
		if pass++; pass != 1 {
			return
		}

		sts, lst = yyS, last
	}

	err := parser.FakeCompile(file, []byte(src), false)
	if err != nil {
		for _, v := range err.(scanner.ErrorList) {
			fmt.Fprintln(os.Stderr, v)
		}
		os.Exit(1)
	}

	if pass == 0 {
		fmt.Fprintf(os.Stderr, "no error for \n%q\n", src)
		os.Exit(1)
	}

	return
}

func patStates(a [][]int, last []int, msg string) pat {
	v0 := last[0]
	for _, v := range last[1:] {
		if v != v0 {
			last[0] = -1 // ambiguous last token
			break
		}
	}
	i := 0
outer:
	for {
		for j, v := range a {
			if i >= len(v) {
				break outer
			}

			switch j {
			case 0:
				v0 = v[i]
			default:
				if vn := v[i]; vn != v0 {
					break outer
				}
			}
		}
		i++
	}

	if i == 0 {
		fmt.Fprintf(os.Stderr, "defs error: can't find a pattern\n")
		fmt.Fprintf(os.Stderr, "%#v\n", a)
		fmt.Fprintf(os.Stderr, "%#v\n", last)
		fmt.Fprintf(os.Stderr, "%q\n", msg)
		os.Exit(1)
	}

	return pat{a[0][:i], last[0], msg}
}

func main() {
	flag.Parse()
	if n := flag.NArg(); n != 0 {
		fmt.Fprintf(os.Stderr, "expected no input file(s), got %d\n", n)
		os.Exit(1)
	}

	var pats []pat

	for _, def := range defs {
		var stats [][]int
		var lsts []int
		for _, src := range def.src {
			s, l := states("package main\n" + src)
			stats = append(stats, s)
			lsts = append(lsts, l)
		}
		pats = append(pats, patStates(stats, lsts, def.msg))
	}

	f := strutil.IndentFormatter(os.Stdout, "\t")
	f.Format("func init() {\n%i")
	f.Format("xErrs = []xErr{\n%i")
	for _, v := range pats {
		f.Format("{\n%istates: []int{")
		for _, v := range v.states {
			f.Format("%d, ", v)
		}
		f.Format("},\n")
		f.Format("last: %d,\n", v.last)
		f.Format("msg: %q,\n", v.msg)
		f.Format("%u},\n")
	}
	f.Format("%u}\n")
	f.Format("%u}\n")
}
